#include "StdAfx.hpp"
#include "VariableManager.hpp"

using namespace common;


////////////////////////////////////////////////////////////////////////////////
// class Variable

void Variable::Hash(common::Hash_Calc &calc)
{
	calc.WriteEx(m_Flags);
	calc.Write(m_Name.data(), m_Name.length());
	ExtraHash(calc);
}

void Variable::LoadFromString( IParamObject &obj, const string &s )
{
	Tokenizer tokenizer(&s, Tokenizer::FLAG_MULTILINE_STRINGS);
	LoadText(obj, tokenizer);
}

void Variable::SaveToString( const IParamObject &obj, string &out )
{
	TokenWriter writer(&out);
	SaveText(obj, writer);
}


////////////////////////////////////////////////////////////////////////////////
// class VariableManager

VariableManager::VariableManager()
: m_Initialized(false)
, m_Hash(0)
{

}

VariableManager::~VariableManager()
{
	for (uint i = m_Variables.size(); i--; )
		delete m_Variables[i];
}

void VariableManager::AddVar( Variable *var )
{
	assert(!m_Initialized);
	assert(!var->GetName().empty());
	m_Variables.push_back(var);

	VarVectorType::iterator byNameIter = FirstNotLessIndex(m_VariablesByName.begin(), m_VariablesByName.end(), var->GetName(), &Variable::NameCmp);
	assert(byNameIter == m_VariablesByName.end() || Variable::NameCmp(var->GetName(), *byNameIter) != 0);
	m_VariablesByName.insert(byNameIter, var);
}

void VariableManager::FinishInit()
{
	assert(!m_Initialized);
	Hash();
	m_Initialized = true;
}

void VariableManager::LoadBinary( IParamObject &obj, common::Stream &stream )
{
	assert(m_Initialized);
	uint hash;
	stream.MustRead(&hash, sizeof(hash));
	assert(hash == m_Hash);

	for (size_t i = 0; i < m_Variables.size(); i++)
		if (!m_Variables[i]->IsReadOnly())
			m_Variables[i]->LoadBinary(obj, stream);
}

void VariableManager::SaveBinary( const IParamObject &obj, common::Stream &stream )
{
	assert(m_Initialized);
	stream.Write(&m_Hash, sizeof(m_Hash));

	for (uint i = 0; i < m_Variables.size(); i++)
		m_Variables[i]->SaveBinary(obj, stream);
}

Variable * VariableManager::FindVarByName( const string &name )
{
	if (name.empty())
		return NULL;

	VarVectorType::iterator it = BinarySearch(m_VariablesByName.begin(), m_VariablesByName.end(), name, &Variable::NameCmp);
	if (it == m_VariablesByName.end())
		return NULL;
	else
		return *it;
}

void VariableManager::Hash()
{
	common::Hash_Calc calc;

	for (size_t i = 0; i < m_Variables.size(); i++)
		m_Variables[i]->Hash(calc);

	m_Hash = calc.Finish();
}

void VariableManager::LoadText( IParamObject &obj, common::Tokenizer &tokenizer )
{
	while (!tokenizer.QueryEOF() && !tokenizer.QuerySymbol('}'))
	{
		tokenizer.AssertToken(Tokenizer::TOKEN_STRING);
		const string &varName = tokenizer.GetString();
		Variable *var = FindVarByName(varName);
		tokenizer.Next();
		tokenizer.AssertSymbol('=');
		tokenizer.Next();

		if (var && !var->IsReadOnly())
			var->LoadText(obj, tokenizer);
		else
			SkipValue(tokenizer);
	}
}

void VariableManager::SkipValue(common::Tokenizer &tokenizer)
{
	uint level = tokenizer.QuerySymbol('{') ? 1 : 0;
	tokenizer.Next();
	while (level > 0)
	{
		if (tokenizer.QueryEOF())
			tokenizer.CreateError("Unexpected end of data.");

		if (tokenizer.QuerySymbol('{'))
			level++;
		else if (tokenizer.QuerySymbol('}'))
			level--;
		tokenizer.Next();
	}
}

void VariableManager::SaveText( const IParamObject &obj, TokenWriter &writer )
{
	for (uint i = 0; i < m_Variables.size(); i++)
	{
		writer.WriteString(m_Variables[i]->GetName());
		writer.WriteSymbol('=');
		m_Variables[i]->SaveText(obj, writer);
		writer.WriteEOL();
	}
}

void VariableManager::LoadFromString( IParamObject &obj, const string &s )
{
	Tokenizer tokenizer(&s, Tokenizer::FLAG_MULTILINE_STRINGS);
	LoadText(obj, tokenizer);
}

void VariableManager::SaveToString( const IParamObject &obj, string &out )
{
	TokenWriter writer(&out);
	SaveText(obj, writer);
}

void VariableManager::ConfigTokenWriter( TokenWriter &writer )
{
	writer.RegisterSymbol('{', true, true, 1);
	writer.RegisterSymbol('}', true, true, -1);
	writer.RegisterSymbol('=', true, true, 0);
	writer.RegisterSymbol(',', false, true, 0);
	writer.RegisterSymbol(';', false, true, 0);
}
